//! 压缩工具
//! 
//! 提供ZIP等压缩功能

use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::Path;
use zip::{ZipArchive, ZipWriter, write::FileOptions, CompressionMethod};

/// ZIP错误类型
#[derive(Debug, thiserror::Error)]
pub enum ZipError {
    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),
    #[error("ZIP error: {0}")]
    ZipError(#[from] zip::result::ZipError),
    #[error("File not found: {0}")]
    FileNotFound(String),
    #[error("Invalid path: {0}")]
    InvalidPath(String),
}

pub type ZipResult<T> = Result<T, ZipError>;

/// ZIP压缩功能
/// 
/// 将多个文件压缩为一个ZIP文件
/// 
/// # Examples
/// ```no_run
/// use librarys::io::zip_files;
/// 
/// let files = vec!["file1.txt", "file2.txt"];
/// zip_files(&files, "output.zip").unwrap();
/// ```
pub fn zip_files(files: &[&str], output: &str) -> ZipResult<()> {
    let path = Path::new(output);
    let file = File::create(path)?;
    let mut zip = ZipWriter::new(BufWriter::new(file));
    
    let options = FileOptions::default()
        .compression_method(CompressionMethod::Deflated)
        .unix_permissions(0o755);
    
    for &file_path in files {
        let file_path = Path::new(file_path);
        
        if !file_path.exists() {
            return Err(ZipError::FileNotFound(file_path.display().to_string()));
        }
        
        let file_name = file_path.file_name()
            .ok_or_else(|| ZipError::InvalidPath(format!("Invalid file name: {}", file_path.display())))?;
        
        zip.start_file(file_name.to_string_lossy(), options)?;
        
        let mut f = File::open(file_path)?;
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer)?;
        zip.write_all(&buffer)?;
    }
    
    zip.finish()?;
    Ok(())
}

/// ZIP解压功能
/// 
/// 将ZIP文件解压到指定目录
/// 
/// # Examples
/// ```no_run
/// use librarys::io::unzip_file;
/// 
/// unzip_file("archive.zip", "./output_dir").unwrap();
/// ```
pub fn unzip_file(zip_path: &str, output_dir: &str) -> ZipResult<()> {
    let zip_path = Path::new(zip_path);
    let output_dir = Path::new(output_dir);
    
    if !zip_path.exists() {
        return Err(ZipError::FileNotFound(zip_path.display().to_string()));
    }
    
    // 创建输出目录
    if !output_dir.exists() {
        std::fs::create_dir_all(output_dir)?;
    }
    
    let file = File::open(zip_path)?;
    let reader = BufReader::new(file);
    let mut archive = ZipArchive::new(reader)?;
    
    for i in 0..archive.len() {
        let mut file = archive.by_index(i)?;
        let output_path = output_dir.join(file.mangled_name());
        
        if file.is_dir() {
            // 创建目录
            std::fs::create_dir_all(&output_path)?;
        } else {
            // 创建父目录（如果需要）
            if let Some(parent) = output_path.parent() {
                if !parent.exists() {
                    std::fs::create_dir_all(parent)?;
                }
            }
            
            // 解压文件
            let mut output_file = File::create(&output_path)?;
            std::io::copy(&mut file, &mut output_file)?;
        }
    }
    
    Ok(())
}

/// 获取ZIP文件信息
/// 
/// 返回文件列表和总大小
/// 
/// # Examples
/// ```no_run
/// use librarys::io::get_zip_info;
/// 
/// let (files, total_size) = get_zip_info("archive.zip").unwrap();
/// println!("文件数量: {}, 总大小: {} 字节", files.len(), total_size);
/// ```
pub fn get_zip_info(zip_path: &str) -> ZipResult<(Vec<String>, u64)> {
    let zip_path = Path::new(zip_path);
    
    if !zip_path.exists() {
        return Err(ZipError::FileNotFound(zip_path.display().to_string()));
    }
    
    let file = File::open(zip_path)?;
    let reader = BufReader::new(file);
    let mut archive = ZipArchive::new(reader)?;
    
    let mut files = Vec::new();
    let mut total_size = 0;
    
    for i in 0..archive.len() {
        let file = archive.by_index(i)?;
        files.push(file.name().to_string());
        total_size += file.size();
    }
    
    Ok((files, total_size))
}

/// 创建ZIP文件从目录
/// 
/// 递归压缩整个目录
/// 
/// # Examples
/// ```no_run
/// use librarys::io::zip_directory;
/// 
/// zip_directory("./source_dir", "output.zip").unwrap();
/// ```
pub fn zip_directory(source_dir: &str, output: &str) -> ZipResult<()> {
    let source_path = Path::new(source_dir);
    let output_path = Path::new(output);
    
    if !source_path.exists() || !source_path.is_dir() {
        return Err(ZipError::FileNotFound(source_path.display().to_string()));
    }
    
    let file = File::create(output_path)?;
    let mut zip = ZipWriter::new(BufWriter::new(file));
    
    let options = FileOptions::default()
        .compression_method(CompressionMethod::Deflated)
        .unix_permissions(0o755);
    
    zip_directory_recursive(&mut zip, source_path, source_path, &options)?;
    
    zip.finish()?;
    Ok(())
}

fn zip_directory_recursive(
    zip: &mut ZipWriter<BufWriter<File>>, 
    dir: &Path, 
    base_path: &Path, 
    options: &FileOptions
) -> ZipResult<()> {
    for entry in std::fs::read_dir(dir)? {
        let entry = entry?;
        let path = entry.path();
        
        let relative_path = path.strip_prefix(base_path)
            .map_err(|_| ZipError::InvalidPath(path.display().to_string()))?;
        
        if path.is_dir() {
            // 添加目录条目
            let dir_name = format!("{}/", relative_path.display());
            zip.add_directory(dir_name, *options)?;
            
            // 递归处理子目录
            zip_directory_recursive(zip, &path, base_path, options)?;
        } else {
            // 添加文件
            zip.start_file(relative_path.to_string_lossy(), *options)?;
            
            let mut f = File::open(&path)?;
            let mut buffer = Vec::new();
            f.read_to_end(&mut buffer)?;
            zip.write_all(&buffer)?;
        }
    }
    
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use tempfile::TempDir;

    #[test]
    fn test_zip_operations() {
        let temp_dir = TempDir::new().unwrap();
        let temp_path = temp_dir.path();
        
        // 创建测试文件
        let file1_path = temp_path.join("file1.txt");
        let file2_path = temp_path.join("file2.txt");
        let zip_path = temp_path.join("test.zip");
        let extract_dir = temp_path.join("extracted");
        
        fs::write(&file1_path, "Hello, World!").unwrap();
        fs::write(&file2_path, "Rust is awesome!").unwrap();
        
        // 测试压缩
        let files = vec![file1_path.to_str().unwrap(), file2_path.to_str().unwrap()];
        let result = zip_files(&files, zip_path.to_str().unwrap());
        assert!(result.is_ok());
        assert!(zip_path.exists());
        
        // 测试获取ZIP信息
        let (file_list, total_size) = get_zip_info(zip_path.to_str().unwrap()).unwrap();
        assert_eq!(file_list.len(), 2);
        assert!(total_size > 0);
        
        // 测试解压
        let result = unzip_file(zip_path.to_str().unwrap(), extract_dir.to_str().unwrap());
        assert!(result.is_ok());
        
        let extracted_file1 = extract_dir.join("file1.txt");
        let extracted_file2 = extract_dir.join("file2.txt");
        assert!(extracted_file1.exists());
        assert!(extracted_file2.exists());
        
        let content1 = fs::read_to_string(&extracted_file1).unwrap();
        let content2 = fs::read_to_string(&extracted_file2).unwrap();
        assert_eq!(content1, "Hello, World!");
        assert_eq!(content2, "Rust is awesome!");
    }

    #[test]
    fn test_zip_directory() {
        let temp_dir = TempDir::new().unwrap();
        let temp_path = temp_dir.path();
        
        // 创建测试目录结构
        let source_dir = temp_path.join("source");
        let sub_dir = source_dir.join("subdir");
        fs::create_dir_all(&sub_dir).unwrap();
        
        fs::write(source_dir.join("file1.txt"), "content1").unwrap();
        fs::write(sub_dir.join("file2.txt"), "content2").unwrap();
        
        let zip_path = temp_path.join("directory.zip");
        
        // 测试目录压缩
        let result = zip_directory(source_dir.to_str().unwrap(), zip_path.to_str().unwrap());
        assert!(result.is_ok());
        assert!(zip_path.exists());
    }
}